package org.example.spring.simple.framework.aop.support;

import org.example.spring.simple.framework.aop.config.ZAopConfig;
import org.example.spring.simple.framework.aop.intercept.ZAfterThrowingAdviceInterceptor;
import org.example.spring.simple.framework.aop.intercept.ZAfterReturningAdviceInterceptor;
import org.example.spring.simple.framework.aop.intercept.ZBeforeAdviceInterceptor;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对AOP配置的解析
 */
public class ZAdvicedSupport {
    private Class targetClass;
    private Object target;
    private Pattern pointCutClassPattern;
    private ZAopConfig aopConfig;
    //transient:将不需要序列化的属性前添加关键字transient，序列化对象的时候，这个属性就不会被序列化。
    private transient Map<Method, List> methodCache;

    public ZAdvicedSupport(ZAopConfig aopConfig) {
        this.aopConfig = aopConfig;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 根据 AOP 配置，将需要回调的方法封装成一个拦截器链并返回提供给外部获取。
     * @return
     */
    public List getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        List cached = methodCache.get(method);
        //若缓存未命中，则执行下一步处理
        if (cached == null) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cached = methodCache.get(m);
            this.methodCache.put(m, cached);
        }
        return cached;
    }

    /**
     * 判断目标类是否符合切面规则，从而决定是否需要生成代理类，对目标方法进行增强
     * @return
     */
    private void parse() {
        //pointCut表达式
        String pointCut = aopConfig.getPointCut()
                .replaceAll("\\.","\\\\.")
                .replaceAll("\\\\.\\*",".*")
                .replaceAll("\\(","\\\\(")
                .replaceAll("\\)","\\\\)");
        String pointCutForClass = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClass.substring(pointCutForClass.lastIndexOf(" ") + 1));
        try {
            methodCache = new HashMap<>();
            Pattern pattern = Pattern.compile(pointCut);

            Class<?> aspectClass = Class.forName(aopConfig.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<>();
            for (Method m : aspectClass.getMethods()) {
                aspectMethods.put(m.getName(), m);
            }
            for (Method m : targetClass.getMethods()) {
                String methodStr = m.toString();
                if (methodStr.contains("throws")) {
                    methodStr = methodStr.substring(0, methodStr.lastIndexOf("throws")).trim();
                }
                Matcher matcher = pattern.matcher(methodStr);
                if (matcher.matches()) {
                    ////执行器链,满足切面规则的类，添加到aop配置中
                    LinkedList<Object> advices = new LinkedList<>();
                    //把每一个方法包装成 MethodInterceptor
                    //前置通知
                    if (! (null == aopConfig.getAspectBefore() || "".equals(aopConfig.getAspectBefore().trim()))) {
                        //创建一个Advice
                        advices.add(new ZBeforeAdviceInterceptor(aspectMethods.get(aopConfig.getAspectBefore()),
                                aspectClass.newInstance()));
                    }
                    //后置通知
                    if (! (null == aopConfig.getAspectAfter() || "".equals(aopConfig.getAspectAfter().trim()))) {
                        //创建一个Advice
                        advices.add(new ZAfterReturningAdviceInterceptor(aspectMethods.get(aopConfig.getAspectAfter()),
                                aspectClass.newInstance()));
                    }
                    //异常通知
                    if (! (null == aopConfig.getAspectAfterThrow() || "".equals(aopConfig.getAspectAfterThrow().trim()))) {
                        //创建一个Advice
                        ZAfterThrowingAdviceInterceptor throwingAdvice = new ZAfterThrowingAdviceInterceptor(aspectMethods.get(aopConfig.getAspectAfterThrow()),
                                aspectClass.newInstance());
                        throwingAdvice.setThrowingName(aopConfig.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    methodCache.put(m, advices);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

}
